package middle;

import java.util.LinkedList;
import java.util.Queue;

/**
 * MT3 拜访
 * @author d3y1
 */
public class MT3{
    private Step start;
    private Step end;

    private Queue<Step> queue = new LinkedList<>();

    private final int[] dx = {0, 0, -1, 1};
    private final int[] dy = {1, -1, 0, 0};

    private int min = Integer.MAX_VALUE;
    private int result = 0;

    private boolean[][] isVisited;
    private int[][] dp;
    private int[][] dist;

    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     *
     * @param CityMap int整型二维数组
     * @param n int整型
     * @param m int整型
     * @return int整型
     */
    public int countPath (int[][] CityMap, int n, int m) {
        // return solution1(CityMap, n, m);
        return solution2(CityMap, n, m);
    }

    /**
     * 广度优先搜索(BFS)
     * 适合最短路径
     *
     * @param CityMap
     * @param n
     * @param m
     * @return
     */
    private int solution1(int[][] CityMap, int n, int m){
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                if(CityMap[i][j] == 1){
                    start = new Step(i, j, 0);
                }
                if(CityMap[i][j] == 2){
                    end = new Step(i, j);
                }
            }
        }

        queue.add(start);
        bfs1(CityMap, n, m);

        return result;
    }

    /**
     * 广度优先搜索(BFS) + 动态规划
     * dp[i][j]表示到达点(i,j)处的最短路径的条数
     *
     * @param CityMap
     * @param n
     * @param m
     * @return
     */
    private int solution2(int[][] CityMap, int n, int m){
        isVisited = new boolean[n][m];
        dp = new int[n][m];
        dist = new int[n][m];

        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                if(CityMap[i][j] == 1){
                    start = new Step(i, j);
                    dp[i][j] = 1;
                    dist[i][j] = 0;
                }
                if(CityMap[i][j] == 2){
                    end = new Step(i, j);
                }
            }
        }

        queue.offer(start);
        isVisited[start.x][start.y] = true;
        bfs2(CityMap, n, m);

        return result;
    }

    /**
     * 广度优先搜索(BFS)
     *
     * @param CityMap
     * @param n
     * @param m
     */
    private void bfs1(int[][] CityMap, int n, int m){
        while(!queue.isEmpty()){
            Step curr = queue.poll();

            if(curr.level > min){
                break;
            }

            if(curr.equals(end)){
                min = curr.level;
                result++;
                continue;
            }

            Step next;
            int x,y;
            for(int i=0; i<4; i++){
                x = curr.x+dx[i];
                y = curr.y+dy[i];
                if(canGo(CityMap, x, y, n, m)){
                    next = new Step(x, y, curr.level+1);
                    queue.add(next);
                }
            }
        }
    }

    /**
     * 广度优先搜索(BFS) + 动态规划
     *
     * @param CityMap
     * @param n
     * @param m
     */
    private void bfs2(int[][] CityMap, int n, int m){
        while(!queue.isEmpty()){
            Step curr = queue.poll();

            if(curr.equals(end)){
                result = dp[end.x][end.y];
                break;
            }

            Step next;
            int x,y;
            for(int i=0; i<4; i++){
                x = curr.x+dx[i];
                y = curr.y+dy[i];
                if(canGo(CityMap, x, y, n, m)){
                    if(dist[x][y]==0 || dist[x][y]==dist[curr.x][curr.y]+1){
                        dist[x][y] = dist[curr.x][curr.y]+1;
                        dp[x][y] += dp[curr.x][curr.y];
                    }

                    if(!isVisited[x][y]){
                        next = new Step(x, y);
                        queue.offer(next);
                        isVisited[x][y] = true;
                    }
                }
            }
        }
    }

    /**
     * 能否移动
     * @param CityMap
     * @param x
     * @param y
     * @param n
     * @param m
     * @return
     */
    private boolean canGo(int[][] CityMap, int x, int y, int n, int m){
        // 行x越界
        if(x<0 || n-1<x){
            return false;
        }

        // 列y越界
        if(y<0 || m-1<y){
            return false;
        }

        // -1 代表不能经过的地区
        if(CityMap[x][y] == -1){
            return false;
        }

        return true;
    }

    private class Step {
        int x;
        int y;
        int level;

        public Step(int x, int y){
            this.x = x;
            this.y = y;
        }

        public Step(int x, int y, int level){
            this.x = x;
            this.y = y;
            this.level = level;
        }

        @Override
        public boolean equals(Object o){
            if(this == o){
                return true;
            }
            if(o == null || getClass() != o.getClass()){
                return false;
            }
            Step step = (Step) o;
            return x == step.x && y == step.y;
        }
    }
}